package org.liuqm.filter;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.liuqm.common.AjaxResult;
import org.liuqm.pojo.Auction;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 分发过滤器
 */
@WebFilter(urlPatterns = "/*")
public class DispatchServlet implements Filter {
    Map<String, String> urlMapping = new HashMap<String, String>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
//        urlMapping.put("/auction/update", "org.liuqm.servlet.AuctionController.updateAuction");
//        urlMapping.put("/auction/add", "org.liuqm.servlet.AuctionController.addAuction");
//        urlMapping.put("/auction/del", "org.liuqm.servlet.AuctionController.delAuction");
//        urlMapping.put("/auction/list", "org.liuqm.servlet.AuctionController.getAuctionList");
//        urlMapping.put("/auctionuser/list", "org.liuqm.servlet.AuctionuserController.getAuctionUserList");
        try {
            urlMapping.putAll(RequestMappingUtils.getUrlMapping());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        if (response.getContentType() == null) {
            // 设置响应数据的内容类型为application/json;
            response.setContentType("application/json;charset=UTF-8");
        }

        String urlStr = req.getRequestURI();
        String classMethodPath = urlMapping.get(urlStr);
        PrintWriter out = response.getWriter();

        if (classMethodPath == null) {
            AjaxResult ajaxResult = new AjaxResult();
            ajaxResult.setCode(404);
            ajaxResult.setMsg("请求路径不存在");
            out.println(JSON.toJSONString(ajaxResult));
            return;
        }
        String method = classMethodPath.substring(classMethodPath.lastIndexOf(".") + 1);
        String classStr = classMethodPath.substring(0, classMethodPath.lastIndexOf("."));


        try {
            Class clazz = Class.forName(classStr);
            //实例化
            Object o = clazz.getConstructor().newInstance();

            //获取需要调用的方法
            List<Method> methodList = Arrays.stream(clazz.getMethods()).filter(method1 -> {
                return method1.getName().equals(method);
            }).toList();

            if (!methodList.isEmpty()) {
                Parameter[] parameters = methodList.get(0).getParameters();

                //通过request获取提交参数
                Object[] paramValue = getParamValue(req, methodList.get(0));

                //得到参数返回值
                AjaxResult result = (AjaxResult) methodList.get(0).invoke(o, paramValue);


                out.println(JSON.toJSONString(result));
            }


        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }


    }

    /**
     * 获取方法的所有参数映射
     * @param request
     * @param method
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IOException
     */
    private static Object[] getParamValue(HttpServletRequest request, Method method) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {

        //通过反射获得方法的所有参数对象
        Parameter[] parameters = method.getParameters();
        Object[] values = new Object[parameters.length];

        if (values.length == 0) {
            return values;
        }

        String contentType = request.getHeader("content-type");
        boolean isGson = contentType != null && contentType.contains("application/json");
        JSONObject entries = new JSONObject();
        if (isGson) {
            String params = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
            // 3、将请求参数字符串转换成JSONObject对象，之后从JSONObject对象中取出值
            entries = JSONUtil.parseObj(params);
        }


        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Class clazz = parameter.getType();
            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
            String paramName = requestParam.name();
            if (!clazz.getPackage().getName().equals("java.lang")) {

                values[i] = clazz.getConstructor().newInstance();
                Method[] methods = clazz.getMethods();
                for (Method method1 : methods) {
                    if (method1.getName().startsWith("set")) {
                        String fileName = method1.getName().substring(3);
                        paramName = fileName.substring(0, 1).toLowerCase() + fileName.substring(1);
                        Object fileValue = null;
                        if (isGson) {
                            fileValue = extractedJson(entries, method1.getParameterTypes()[0].getSimpleName(), paramName);
                        } else {
                            fileValue = extracted(request, method1.getParameterTypes()[0].getSimpleName(), paramName);
                        }
                        method1.invoke(values[i], fileValue);
                    }
                }
            } else {
                if (isGson) {
                    values[i] = extractedJson(entries, clazz.getSimpleName(), paramName);
                } else {
                    values[i] = extracted(request, clazz.getSimpleName(), paramName);
                }
            }
        }
        return values;

    }

    /**
     * 获取json数据
     * @param entries
     * @param paramType
     * @param paramName
     * @return
     * @throws IOException
     */
    private static Object extractedJson(JSONObject entries, String paramType, String paramName) throws IOException {
        Object resultVlaue = null;


        switch (paramType) {
            case "String":
                resultVlaue = entries.getStr(paramName);
                break;
            case "Integer":
                resultVlaue = entries.getInt(paramName);
                break;
            case "int":
                resultVlaue = entries.getInt(paramName);
                break;
            case "Double":
                resultVlaue = entries.getDouble(paramName);
                break;
            case "double":
                resultVlaue = entries.getDouble(paramName);
                break;
            case "Long":
                resultVlaue = entries.getLong(paramName);
                break;
            case "long":
                resultVlaue = entries.getLong(paramName);
                break;
            case "Boolean":
                resultVlaue = entries.getBool(paramName);
                break;
            case "boolean":
                resultVlaue = entries.getBool(paramName);
                break;
            default:
                resultVlaue = entries.getStr(paramName);
                break;
        }


        return resultVlaue;
    }

    /**
     * 获取查询参数中的数据
     * @param request
     * @param paramType
     * @param paramName
     * @return
     * @throws IOException
     */
    private static Object extracted(HttpServletRequest request, String paramType, String paramName) throws IOException {
        Object resultVlaue = null;


        switch (paramType) {
            case "String":
                resultVlaue = request.getParameter(paramName);
                break;
            case "Integer":
                resultVlaue = request.getParameter(paramName) == null ? null : Integer.parseInt(request.getParameter(paramName));
                break;
            case "int":
                resultVlaue = request.getParameter(paramName) == null ? 0 : Integer.parseInt(request.getParameter(paramName));
                break;
            case "Double":
                resultVlaue = request.getParameter(paramName) == null ? null : Double.parseDouble(request.getParameter(paramName));
                break;
            case "double":
                resultVlaue = request.getParameter(paramName) == null ? 0 : Double.parseDouble(request.getParameter(paramName));
                break;
            case "Long":
                resultVlaue = request.getParameter(paramName) == null ? null : Long.parseLong(request.getParameter(paramName));
                break;
            case "long":
                resultVlaue = request.getParameter(paramName) == null ? 0 : Long.parseLong(request.getParameter(paramName));
                break;
            case "Boolean":
                resultVlaue = request.getParameter(paramName) == null ? null : Boolean.parseBoolean(request.getParameter(paramName));
                break;
            case "boolean":
                resultVlaue = request.getParameter(paramName) == null ? false : Boolean.parseBoolean(request.getParameter(paramName));
                break;
            default:
                resultVlaue = request.getParameter(paramName);
                break;
        }


        return resultVlaue;
    }
}
